package com.xbe.netty;

import com.xbe.common.utils.DateUtils;
import com.xbe.equipment.domain.Equipment;
import com.xbe.equipment.service.IEquipmentService;
import com.xbe.system.domain.SysUser;
import com.xbe.system.service.ISysUserService;
import com.xbe.userSim.domain.UserSim;
import com.xbe.userSim.service.IUserSimService;
import com.xbe.visibility.domain.Visibility;
import com.xbe.visibility.service.IVisibilityService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @date: 2019/10/8
 */
@Component
@ChannelHandler.Sharable
public class NettyServerHandler extends ChannelInboundHandlerAdapter
{

    private static final Logger received_packet_log = LoggerFactory.getLogger("received-packet");
    private static final Logger send_order_log = LoggerFactory.getLogger("send-order");
    //卡号 管道
    public static Map<String, ChannelHandlerContext> map = new ConcurrentHashMap<>();
    //
    public static Map<String, String> ipMap = new ConcurrentHashMap<String, String>();
    //离线状态检查周期  五分钟
    public static int expiredSim = 60 * 5;
    private static Set sims = new HashSet();
    //手动模式命令前缀
    private static String MANUAL_PREFIX = "AA;0C;02;01;";
    //手动模式命令后缀
    private static String MANUAL_SUFFIX = "05;";
    //道路强化模式前缀
    private static String MANUAL_ROAD_PREFIX = NettyServerHandler.MANUAL_PREFIX + "01;";
    //主动诱导模式前缀
    private static String MANUAL_INDUCE_PREFIX = NettyServerHandler.MANUAL_PREFIX + "02;";
    //防止追尾模式前缀
    private static String MANUAL_REAR_END_PREFIX = NettyServerHandler.MANUAL_PREFIX + "03;";
    //自动命令
    private static String AUTO_ORDER = "AA;0C;02;01;04;  ;  ;  ;01;  ;  ;  ;  ;05;";
    private static String CLOSE_ORDER = "AA;0C;02;01;04;00;00;00;00;00;00;00;00;05;";
    //关闭命令
    //索要电压等数据命令
    private static String EMPTY_ORDER = "AA;08;01;00;00;00;00;00;00;05;";
    //传入整个环境
    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private IEquipmentService equipmentService;

    @Autowired
    private NettyServer nettyServer;

    @Autowired
    private IUserSimService userSimService;

    @Autowired
    private IVisibilityService visibilityService;


    @Autowired
    private ISysUserService userService;

    @Autowired
    private StringRedisTemplate redisTemplate;


    //对时命令
    public static Map checkTime(String sim)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {
            StringBuilder sb = new StringBuilder();
            sb.append("AA;");
            sb.append("09;");
            sb.append("03;");
            String current_time = DateUtils.parseDateToStr("yy-MM-dd-HH-mm-ss",
                                                           DateUtils.getNowDate());
            String[] current_times = current_time.split("-");
            sb.append(current_times[0] + ";");
            sb.append(current_times[1] + ";");
            sb.append(current_times[2] + ";");
            sb.append(current_times[3] + ";");
            sb.append(current_times[4] + ";");
            sb.append(current_times[5] + ";");
            sb.append("AA;");
            sb.append("05;");
            NettyServerHandler.send_order_log.info("【对时命令】【{}】【{}】", sim, sb.toString());

            Map map = NettyServerHandler.insertOrder(true, "对时命令", "发送成功");

            ctx.channel().write(sb.toString());
            ctx.channel().flush();


            return map;

        }
        Map map = NettyServerHandler.insertOrder(false, "对时命令", "发送失败");
        return map;


    }


    private static Map insertOrder(boolean b, String orderName, String state)
    {
        Map map = new HashMap();
        map.put("flag", b);
        map.put("orderName", orderName);
        map.put("state", state);
        return map;

    }

    // 发现没有所需要的电压等数据的时候，那么就给设备发送数据
    public static boolean sendEmpty(String sim)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {
            NettyServerHandler.send_order_log.info("【索要数据】【{}】【{}】", sim,
                                                   NettyServerHandler.EMPTY_ORDER);
            ctx.channel().write(NettyServerHandler.EMPTY_ORDER);
            ctx.channel().flush();
            return true;
        }
        return false;
    }

    // 工作模式---关闭
    public static Map send_workPattern_close(String sim)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {

            NettyServerHandler.send_order_log.info("【关闭模式】【{}】【{}】", sim,
                                                   NettyServerHandler.CLOSE_ORDER);
            Map map = NettyServerHandler.insertOrder(true, "关闭模式", "发送成功");
            ctx.channel().write(NettyServerHandler.CLOSE_ORDER);
            ctx.channel().flush();


            return map;
        }
        Map map = NettyServerHandler.insertOrder(false, "关闭模式", "发送失败");
        return map;

    }

    // 工作模式---自动
    public static Map send_workPattern_auto(String sim)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {


            NettyServerHandler.send_order_log.info("【自动模式】【{}】【{}】", sim,
                                                   NettyServerHandler.AUTO_ORDER);
            Map map = NettyServerHandler.insertOrder(true, "自动模式", "发送成功");
            ctx.channel().write(NettyServerHandler.AUTO_ORDER);
            ctx.channel().flush();

            return map;

        }
        Map map = NettyServerHandler.insertOrder(false, "自动模式", "发送失败");
        return map;


    }

    // 工作模式---手动--道路强化模式
    public static Map send_workPattern_manual_road(String sim, String brightness,
                                                   String timePattern, String startTime,
                                                   String endTime)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {
            StringBuilder sb = new StringBuilder();
            sb.append(NettyServerHandler.MANUAL_ROAD_PREFIX);


            switch (brightness)
            {
                default:
                    sb.append("  ;");
                    break;
                case "1":
                    sb.append("41;");
                    break;
                case "2":
                    sb.append("42;");
                    break;
                case "3":
                    sb.append("43;");
                    break;
                case "4":
                    sb.append("44;");
                    break;
                case "5":
                    sb.append("45;");
                    break;
                case "6":
                    sb.append("46;");
                    break;
                case "7":
                    sb.append("47;");
                    break;
                case "8":
                    sb.append("48;");
                    break;
            }
            sb.append("  ;");
            sb.append("  ;");

            NettyServerHandler.timePattern(timePattern, startTime, endTime, sb);
            sb.append(NettyServerHandler.MANUAL_SUFFIX);

            NettyServerHandler.send_order_log.info("【手动-道路强化】【{}】【{}】", sim, sb.toString());

            Map map = NettyServerHandler.insertOrder(true, "手动|道路强化|", "发送成功");
            ctx.channel().write(sb.toString());
            ctx.channel().flush();

            return map;

        }
        Map map = NettyServerHandler.insertOrder(false, "手动|道路强化|", "发送失败");
        return map;

    }

    // 工作模式---手动--主动诱导模式
    public static Map send_workPattern_manual_induce(String sim, String brightness,
                                                     String frequency, String timePattern,
                                                     String startTime, String endTime)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {
            StringBuilder sb = new StringBuilder();
            sb.append(NettyServerHandler.MANUAL_INDUCE_PREFIX);
            switch (brightness)
            {
                default:
                    sb.append("  ;");
                    break;
                case "1":
                    sb.append("41;");
                    break;
                case "2":
                    sb.append("42;");
                    break;
                case "3":
                    sb.append("43;");
                    break;
                case "4":
                    sb.append("44;");
                    break;
                case "5":
                    sb.append("45;");
                    break;
                case "6":
                    sb.append("46;");
                    break;
                case "7":
                    sb.append("47;");
                    break;
                case "8":
                    sb.append("48;");
                    break;
            }
            switch (frequency)
            {
                default:
                    sb.append("  ;");
                    break;
                case "0.5":
                    sb.append("21;");
                    break;
                case "1":
                    sb.append("22;");
                    break;
                case "2":
                    sb.append("23;");
                    break;
            }
            sb.append("  ;");

            NettyServerHandler.timePattern(timePattern, startTime, endTime, sb);
            sb.append(NettyServerHandler.MANUAL_SUFFIX);


            NettyServerHandler.send_order_log.info("【手动-主动诱导】【{}】【{}】", sim, sb.toString());
            Map map = NettyServerHandler.insertOrder(true, "手动|主动诱导|", "发送成功");
            ctx.channel().write(sb.toString());
            ctx.channel().flush();


            return map;

        }
        Map map = NettyServerHandler.insertOrder(false, "手动|主动诱导|", "发送失败");

        return map;

    }

    // 工作模式---手动--防止追尾模式
    public static Map send_workPattern_manual_rearEnd(String sim, String brightness,
                                                      String frequency, String wake,
                                                      String timePattern, String startTime,
                                                      String endTime)
    {
        ChannelHandlerContext ctx = NettyServerHandler.map.get(sim.trim());
        if (ctx != null)
        {
            StringBuilder sb = new StringBuilder();
            sb.append(NettyServerHandler.MANUAL_REAR_END_PREFIX);
            switch (brightness)
            {
                default:
                    sb.append("  ;");
                    break;
                case "1":
                    sb.append("41;");
                    break;
                case "2":
                    sb.append("42;");
                    break;
                case "3":
                    sb.append("43;");
                    break;
                case "4":
                    sb.append("44;");
                    break;
                case "5":
                    sb.append("45;");
                    break;
                case "6":
                    sb.append("46;");
                    break;
                case "7":
                    sb.append("47;");
                    break;
                case "8":
                    sb.append("48;");
                    break;
            }
            switch (frequency)
            {
                default:
                    sb.append("  ;");
                    break;
                case "0.5":
                    sb.append("21;");
                    break;
                case "1":
                    sb.append("22;");
                    break;
                case "2":
                    sb.append("23;");
                    break;
            }
            switch (wake)
            {
                default:
                    sb.append("  ;");
                    break;
                case "2":
                    sb.append("31;");
                    break;
                case "3":
                    sb.append("32;");
                    break;
                case "4":
                    sb.append("33;");
                    break;
                case "5":
                    sb.append("34;");
                    break;
                case "6":
                    sb.append("35;");
                    break;
            }
            NettyServerHandler.timePattern(timePattern, startTime, endTime, sb);


            sb.append(NettyServerHandler.MANUAL_SUFFIX);


            NettyServerHandler.send_order_log.info("【手动-防止追尾】【{}】【{}】", sim, sb.toString());
            Map map = NettyServerHandler.insertOrder(true, "手动|防止追尾|", "发送成功");
            ctx.channel().write(sb.toString());
            ctx.channel().flush();


            return map;

        }
        Map map = NettyServerHandler.insertOrder(false, "手动|防止追尾|", "发送失败");
        return map;

    }

    //时间模式
    private static void timePattern(String timePattern, String startTime, String endTime,
                                    StringBuilder sb)
    {

        if (timePattern.equals("0"))
        { // 全部无效
            sb.append("00;");
            sb.append("  ;");
            sb.append("  ;");
            sb.append("  ;");
            sb.append("  ;");


        }
        if (timePattern.equals("1"))
        { // 光敏
            sb.append("01;");
            sb.append("  ;");
            sb.append("  ;");
            sb.append("  ;");
            sb.append("  ;");


        }
        if (timePattern.equals("2"))
        { // 星历
            sb.append("02;");

            if (startTime != null && !"".equals(startTime))
            {
                sb.append(startTime.trim().split(":")[0] + ";");
                sb.append(startTime.trim().split(":")[1] + ";");
            }
            else
            {
                sb.append("  ;");
                sb.append("  ;");
            }
            if (endTime != null && !"".equals(endTime))
            {
                sb.append(endTime.trim().split(":")[0] + ";");
                sb.append(endTime.trim().split(":")[1] + ";");
            }
            else
            {
                sb.append("  ;");
                sb.append("  ;");
            }

        }
    }

    public static String replyTime()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("AA;");
        sb.append("09;");
        sb.append("03;");
        String current_time = DateUtils.parseDateToStr("yy-MM-dd-HH-mm-ss", DateUtils.getNowDate());
        String[] current_times = current_time.split("-");
        sb.append(current_times[0] + ";");
        sb.append(current_times[1] + ";");
        sb.append(current_times[2] + ";");
        sb.append(current_times[3] + ";");
        sb.append(current_times[4] + ";");
        sb.append(current_times[5] + ";");
        sb.append("BB;");
        sb.append("05;");

        return sb.toString();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
    {


        ByteBuf buf = (ByteBuf) msg;
//AA;#0001;898604381518C1087179;36.711658;115.877777;12876;00976;;01;04;  ;  ;  ;01;  ;  ;  ;  ;05
        byte[] reg = new byte[buf.readableBytes()];

        buf.readBytes(reg);
        String rcv = new String(reg, "ASCII");
        String[] str = rcv.split(";");
        String head = str[0]; // 起始位
        String name = str[1]; // 设备名字
        String sim = str[2].trim(); // sim卡号
        String lat = str[3]; // 纬度
        String lng = str[4]; // 经度
        String voltage = str[5]; // 电池电压
        String visibility_10 = str[6];
        String visibility_1 = str[7]; // 10分钟能见度 高八位
        String workPattern = str[8];
        String check = str[str.length - 1]; // 校验位
        if (!"AA".equals(head) || !"05".equals(check))
        {

            NettyServerHandler.received_packet_log.info("收到数据包--数据包错误【{}】", rcv);
            return;
        }
        // 1 发送对时指令 第一次收到数据时发送 以后不发送
        firstCheckTime(ctx, sim);
        NettyServerHandler.received_packet_log.info("收到数据包--数据包正确【{}】", rcv);
        Set sims = nettyServer.getSims();

        NettyServerHandler.map.put(sim, ctx);
        //2 存入redis 目的是为了判断在线离线，触发key失效事件
        redisTemplate.opsForValue().set(sim, "-1", NettyServerHandler.expiredSim, TimeUnit.SECONDS);
        //3 卡号存在，更新信息
        if (sims.contains(sim))
        {
            updateEquipmentData(sim, lat, lng, voltage, visibility_10);
        }
        //4 卡号不存在，增加数据
        else
        {
            saveEquipmentData(name, sim, lat, lng, voltage, visibility_10);

        }
        //5 时间能见度添加
        if (!"".equals(visibility_10))
        {
            Visibility visibility = new Visibility();
            visibility.setSim(sim);
            visibility.setSeeing(Integer.parseInt(visibility_10));
            visibilityService.insertVisibility(visibility);
        }


    }

    private void saveEquipmentData(String name, String sim, String lat, String lng,
                                   String voltage, String visibility_10)
    {
        Equipment equipment = new Equipment();
        equipment.setSim(sim);
        equipment.setName(name);
        equipment.setLng(lng);
        equipment.setLat(lat);
        if (voltage != null && !"".equals(voltage))
        {
            equipment.setVoltage(voltage); // 电压
        }
        if (visibility_10 != null && !"".equals(visibility_10))
        {
            equipment.setVisibility(visibility_10);// 10分钟能见度
            int v = Integer.parseInt(visibility_10); // 能见度
            if (!"".equals(v))
            {
                if (v >= 500)
                {
                    equipment.setSpeedLimit("120");
                }
                if (v >= 200 && v < 500)
                {
                    equipment.setSpeedLimit("80");
                }
                if (v < 200)
                {
                    equipment.setSpeedLimit("60");
                }
            }
        }
        //在线
        equipment.setState("0");
        equipment.setUpdateTime(new Date());
        UserSim userSim = userSimService.selectUserSimBySim(sim);
        if (userSim != null)
        {
            equipment.setUserId(userSim.getUserId());
            SysUser sysUser = userService.selectUserById(userSim.getUserId());
            equipment.setUserName(sysUser.getUserName());
        }

        equipmentService.insertEquipment(equipment);


    }

    //更新数据
    private void updateEquipmentData(String sim, String lat, String lng, String voltage,
                                     String visibility_10)
    {
        Equipment equipment = equipmentService.selectEquipmentBySim(sim);
        if (lat != null && !"".equals(lat))
        {// 经度
            equipment.setLat(lat);
        }
        if (lng != null && !"".equals(lng))
        {// 纬度
            equipment.setLng(lng);
        }
        if (voltage != null && !"".equals(voltage))
        {
            equipment.setVoltage(voltage); // 电压
        }
        if (visibility_10 != null && !"".equals(visibility_10))
        {
            equipment.setVisibility(visibility_10);// 10分钟能见度

        }
        //在线
        equipment.setState("0");
        equipmentService.updateEquipmentBySim(equipment);


    }

    private void firstCheckTime(ChannelHandlerContext ctx, String sim)
    {
        String ip = NettyServerHandler.ipMap.get(sim.trim());
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIP1 = insocket.getAddress().getHostAddress();
        if (ip == null || !ip.equals(clientIP1))
        {

            ByteBuf respByteBuf = Unpooled.copiedBuffer(NettyServerHandler.replyTime().getBytes());
            ctx.write(respByteBuf);
            NettyServerHandler.send_order_log.info("【首次收到数据回复时间】【{}】【{}】", sim,
                                                   NettyServerHandler.replyTime());

        }
        InetSocketAddress insocket1 = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIP = insocket1.getAddress().getHostAddress();
        NettyServerHandler.ipMap.put(sim.trim(), clientIP);


    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
    {
        /**flush：将消息发送队列中的消息写入到 SocketChannel 中发送给对方，为了频繁的唤醒 Selector 进行消息发送
         * Netty 的 write 方法并不直接将消息写如 SocketChannel 中，调用 write 只是把待发送的消息放到发送缓存数组中，再通过调用 flush
         * 方法，将发送缓冲区的消息全部写入到 SocketChannel 中
         * */
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
    {
        /**当发生异常时，关闭 ChannelHandlerContext，释放和它相关联的句柄等资源 */
        //   ctx.close();
    }


}
